﻿using System;
using System.Web.UI;
using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using Guru.Utilities;
namespace Guru.Web.Controls.Validators
{
    /// <summary>
    /// Base class of validation item    
    /// </summary>
    public  abstract class ValidationItem : StateManager,  IAttributeAccessor
    {
        #region const
        public const char RESOURCE_PARAMS_SPLIT=',';
        public const string GLOBAL_RESOURCE_PREFIX = "$resx$";
        
        /// <summary>
        /// Remove validation rules
        ///     {0}: ClientId of the control
        /// </summary>
        protected const string _JS_INS_REMOVE = @"
            if($('#{0}') != null)
                $('#{0}').rules('remove')";
        /// <summary>
        /// Call validation function
        ///     {0}: ClientId of the control
        /// </summary>
        protected const string _JS_INS_VALIDATE = @"
            if($('#{0}') != null)
                isValid = ($('#{0}').valid()==false)? false : isValid";
        #endregion

        #region Init
        public ValidationItem()
        {           
            Message = string.Empty;
        }
        public ValidationItem(string  controlToValidateId, string message)
        {
            this.ControlToValidateID = controlToValidateId;
            this.Message = message;
        }
        #endregion

        #region Members
        /// <summary>
        /// Find control to validate
        /// </summary>
        public Control ObjectControlToValidate {
            get {
                
                if (string.IsNullOrEmpty(ControlToValidateID))
                    throw new PropertyNullRefException(ControlToValidateID);
                //Find in this container
                Control ctr = this.FindControl(this.Container, this.ControlToValidateID);
                //if not found --> find in parent;
                if (ctr == null)
                {
                    Control container = this.Container.Parent;
                    ctr = this.FindControl(container, this.ControlToValidateID);
                }
                
                //Cannot be found
                if (ctr == null)
                    throw new ControlNotFoundException(ControlToValidateID);
                else
                    return ctr;
            }
        }
       
        #endregion

        #region Properties
        internal Control Container {
            set
            {
                ViewState["Parent"] = value;
            }
            get
            {
                return (Control)ViewState["Parent"];
            }
        }

        /// <summary>
        /// The input control to validate
        /// </summary>
        [Category("Behavior"), DefaultValue(""),
        Description("The input control to validate"), NotifyParentProperty(true)]
        public string ControlToValidateID
        {
            set
            {
                ViewState["ControlToValidateID"] = value;                
            }
            get
            {
                return (string)ViewState["ControlToValidateID"];
            }
        }      

        /// <summary>
        /// Custom validation message
        /// </summary>
        [Category("Behavior"), DefaultValue(""),
        Description("Custom validation message"),NotifyParentProperty(true)]
        public string Message
        {
            set
            {               
                ViewState["Message"] = value;
            }
            get
            {
                string message = (string)ViewState["Message"];
                //if the Message is not null, return the Message
                if (!string.IsNullOrEmpty(message))
                    return message.ToJString();
                else if (!string.IsNullOrEmpty(this.ResMessage))
                {
                    //Get message from Global resource
                    //Resources.[ResourceName].[ResourceKey]
                    int lastDotIndex = this.ResMessage.LastIndexOf('.');
                    string fieldName = this.ResMessage.Remove(0, lastDotIndex + 1);
                    string resourname = this.ResMessage.Substring(0, lastDotIndex);

                    string resMessage = Invoker.GetGlobalResourceValue(resourname, fieldName);
                    //Replace parameters
                    if (!string.IsNullOrEmpty(this.ResParams)) {
                        string[] messageParams = this.ResParams.Split(RESOURCE_PARAMS_SPLIT);
                        resMessage = string.Format(resMessage, messageParams);
                    }
                    return resMessage.ToJString();
                }
                else return string.Empty;
            }
        }

        /// <summary>
        /// The resource key to get the message
        /// Format: Resources.[ResourceName].[ResourceKey]
        /// </summary>
        [Category("Behavior"), DefaultValue(""),
        Description("ResMessage"), NotifyParentProperty(true)]
        public string ResMessage
        {
            set
            {
                ViewState["ResMessage"] = value;
            }
            get
            {
                return (string)ViewState["ResMessage"];
            }
        }

        /// <summary>
        /// Format: value1,value2
        /// </summary>
        public string ResParams
        {
            set
            {
                ViewState["ResParams"] = value;
            }
            get
            {
                if (ViewState["ResParams"] != null)
                    return ((string)ViewState["ResParams"]);
                else return string.Empty;
            }
        }
        
        #endregion

        #region Rendering       
        /// <summary>
        /// Sends server control content to a provided HtmlTextWriter object,
        /// which writes the content to be rendered on the client.
        /// When developing custom items,
        /// must override this method to generate content for an ASP.NET page.
        /// </summary>
        /// <returns></returns>
        public abstract string RenderTrigger(HtmlTextWriter writer);
        public abstract string RenderRuleRegister(HtmlTextWriter writer);
        public virtual string RenderRuleRemove(HtmlTextWriter writer) {
            return string.Format(_JS_INS_REMOVE, this.ObjectControlToValidate.ToJQueryId());
        }
        public virtual string RenderValidIntruction(HtmlTextWriter writer)
        {
            return string.Format(_JS_INS_VALIDATE, this.ObjectControlToValidate.ToJQueryId());
        }
        public virtual string RenderAdditionalMethods(HtmlTextWriter writer)
        {
            return string.Empty;
        }
        #endregion        

        #region IAttributeAccessor implementation
        /*IAttributeAccessor Remarks:
         * If you author a custom server control that inherits from the 
         * WebControl, HtmlControl, or ListItem class,
         * the .NET Framework automatically provides programmatic access to 
         * attributes since each of these classes implement the IAttributeAccessor interface.
         * If you author a custom server control that does not inherit from one of these classes 
         * and plan to allow programmatic access to attributes that do not correspond 
         * with the control's strongly-typed properties,
         * be sure to implement the IAttributeAccessor interface.*/

        /// <summary>
        /// Implement the GetAttribute method for the control. When
        /// this method is called from a page, the values for the control's
        /// properties can be displayed in the page.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public String GetAttribute(String name)
        {
           return (String)ViewState[name];
        }


        /// <summary>
        /// Implement the SetAttribute method for the control. When
        /// this method is called from a page, the control's properties
        /// are set to values defined in the page.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value1"></param>
        public void SetAttribute(String name, String value1)
        {
            ViewState[name] = value1;
        }
        #endregion

        protected Control FindControl(Control container, string controlId)
        {
            Control matchControl = null;
            foreach (Control control in container.Controls)
            {
                if (control is PlaceHolder
                    || control is Panel
                    || control is Content
                    || control is ContentPlaceHolder
                    || control is MasterPage
                    || control is HtmlContainerControl
                    || control is UserControl                  
                )
                {
                    matchControl = FindControl(control, controlId);
                    if (matchControl != null) return matchControl;

                }
                else
                {
                    matchControl = container.FindControl(controlId);
                    if (matchControl != null) return matchControl;
                }
            }
            return matchControl;
        }
       
    }
}
